#include <iostream>
#include <string>
#include <sys/stat.h>
#include <fstream>
#include <cerrno>

#include <istream>
#include "log/loger.hpp"

class FileHelper{

public:
    FileHelper(std::string& filename):_filename(filename){
    }

    /**
     * 判断文件是否存在
     */
    static bool existsFile(const char* filename){
        //使用stat函数
        struct stat st;
        return (stat(filename,&st) == 0);
    }

    /**
     * 判断文件大小，直接通过获取的文件状态返回的对象st
     * 获取其内部成员 st_size 即是文件的大小
     */
    size_t sizeFile(){
        struct stat st;
        stat(_filename.c_str(),&st);
        return  st.st_size;
    }


    /**
     * 修改文件名称
     */
    bool renameFile(const std::string& newfilename){
        //使用函数rename
        return (std::rename(_filename.c_str(),newfilename.c_str()) == 0);//返回0表示修改成功
    }

    /**
     * 删除文件
     */
    static bool removeFile(const char* filename){
        return std::remove(filename) == 0;//返回0表示删除成功
    }

    /**
     * 创建文件
     */
    static bool createFile(const char* filename){
        std::fstream fs(filename, std::ios::binary | std::ios::out);
        //binary二进制打开防止数据出现问题，
        //out：写权限
        return fs.is_open();//通过打开的文件判断是否打开成功
    }

    /**
     * 文件的读取
     * 将读取的文件中的所有内容放进buf中
     * buf：输出型参数，存要读取的数据
     */
    bool readFile(char* buf,size_t offset,size_t len){
        //1. 打开文件
        std::fstream fs(_filename, std::ios::binary | std::ios::in);//二进制和读方式打开文件filename
            // 判断是否打开成功
        if(fs.is_open()){
            ELOG("%s,文件打开失败！",_filename.c_str());
            return false;
        }

        //2. 从当前位置读取所有内容放到buf中
            //2.1 偏移到文件的指定位置
        fs.seekg(offset,ios::beg);//从文件开始beg，偏移到offset

            //2.2 读取所有内容给到buf
        fs.read(buf,len);//读取len字节长度
        if(fs.good() == false){//判断上一步读取是否成功
            ELOG("%s,文件读取数据失败",_filename.c_str());
            return false;
        }
        fs.close();
        return true;
    }
    /**
     * 文件的读取
     * 将读取的文件中的所有内容放进buf中
     * buf：输出型参数，存要读取的数据
     */
    bool readFile(string& buf){
        //获取文件大小
        int filesize = sizeFile();
        //调整body空间：resize
        buf.resize(filesize);
        return readFile(&buf[0],0,filesize);
    }

    /**
     * 文件的写入
     * 将读取的文件中的所有内容放进buf中
     * body：所要写的内容
     * offset：偏移量
     * len：写入长度
     */
    bool writeFile(const string& body,size_t offset,size_t len){
        //1. 打开文件
        std::ofstream fs(_filename.c_str(),std::ios::binary | ios::out);
            // 判断是否打开成功
        if(fs.is_open()){
            ELOG("%s,文件打开失败！",_filename.c_str());
            return false;
        }

        //2. 跳到指定位置写：
        fs.seekp(offset,ios::beg);

        //3. 写
        fs.write(body.c_str(),len);
        if(fs.good() == false){
            ELOG("%s,文件写入数据失败",_filename.c_str());
            return false;
        }
        //3. 关闭
        fs.close();
        return true;
    }

    bool writeFile(const string& body){
        return writeFile(body,0,body.size());        
    }

    /**
     * 创建目录
     * 其中注意的是要从根目录开始创建，这样才能防止父目录不存在，就创建目录了
     * path：路径
    */
    static bool createDirectory(const string& path){
        //循环创建
        size_t pos = -1,idx = 0;
        //pos查找/位置

        pos = path.find_first_of("/");
        while(pos != std::string::npos);
        {
            std::string subpath = path.substr(0,pos);//每次从 0 ~ pos（因为要保证层级结构）
            int ret = mkdir(subpath.c_str(),0775);//创建目录 ，并设置 权限为 775 
            if(ret != 0 && errno != EEXIST){
                ELOG("创建目录 %s 失败",subpath.c_str());
                 return false;
            }
            idx = pos + 1;//idx记录未获取的目录的 / 分割的字符串起始位置
            pos = path.find_first_of("/",idx);
        }
        //当出来时，就表示找不到 "/"
        //也就表示 前面的目录 即使不存在也会被创建好了
        return mkdir(path.c_str(),0775) == 0;///
    }

    /**
     * path：要删除的目录
    */
    static bool removeDirectory(const string& path){
        //此处使用stytem来执行 rm -rf 指令进行删除操作，这样就能减代码量（就不在需要再去写删除多层目录的代码）
        std::string cmd = "rm -rf " + path;
        return system(cmd.c_str()) != -1;
    }

    static string parentDirectory(const string& path){
        //获取父目录
        //直接从后往前找到 / 前面就是父级目录
        int pos = path.find_last_of("/");
        if(pos == std::string::npos){
            //表示当前路径就是最终目录，返回./
            return "./";
        }
        return path.substr(0,pos);
    }

private:
    std::string _filename;
};